package uplb.ics.cs190.e2d.object;

import uplb.ics.cs190.e2d.display.Graphics;
import uplb.ics.cs190.e2d.graphics.Image;

public class Sprite {

	protected Background background;
	
	protected double x;
	protected double y;
	
	protected double horizontalSpeed;
	protected double verticalSpeed;
	
	protected double oldX;
	protected double oldY;
	
	protected static double screenX;
	protected static double screenY;
	
	protected int width;
	protected int height;
	
	protected int id;
	
	protected int layer;
	
	protected boolean active = true;
	protected boolean immutable;
	
	protected transient Image image;
	
	public Sprite(Image image, double x, double y) {
		this.oldX = this.x = x;
		this.oldY = this.y = y;
		
		if(image != null) {
			this.image = image;
		}
	}
	
	public Sprite(Image image) {
		this(image, 0, 0);
	}
	
	public Sprite(double x, double y) {
		this(null, x, y);
	}
	
	public Sprite() {
		this(null, 0, 0);
	}
	
	public void setBackground(Background background) {
		this.background = background;
		if(this.background == null) {
			
		}
	}
	
	public Background getBackground() {
		return this.background;
	}
	
	public Image getImage() {
		return this.image;
	}
	
	public void setImage(Image image) {
		this.image = image;
		
		this.width = this.height = 0;
		if(image != null) {
			
		}
	}
	
	public int getWidth() {
		return this.width;
	}
	
	public int getHeight() {
		return this.height;
	}
	
	public boolean moveTo(long elapsedTime, double x, double y, double speed) {
		if(this.x == x && this.y == y) {
			return true;
		}
		
		double angle = 90 + Math.toDegrees(Math.atan2(y - this.y, x - this.x));
		double radians = Math.toRadians(angle);
		
		double vx = Math.sin(radians) * speed * elapsedTime;
		double vy = -Math.cos(radians) * speed * elapsedTime;
		
		boolean arriveX = false;
		boolean arriveY = false;
		
		if(vx != 0) {
			if(vx > 0) {
				if(this.x + vx >= x) {
					vx = x - this.x;
					arriveX = true;
				}
			} else {
				if(this.x + vx <= x) {
					vx = x - this.x;
					arriveX = true;
				}
			}
		} else if(this.x == x) {
			arriveX = true;
		}
		
		if(vy != 0) {
			if(vy > 0) {
				if(this.y + vy >= y) {
					vy = y - this.y;
					arriveY = true;
				}
			} else {
				if(this.y + vy <= y) {
					vy = y - this.y;
					arriveY = true;
				}
			}
		} else if(this.y == y) {
			arriveY = true;
		}
		
		this.move(vx, vy);
		
		return (arriveX && arriveY);
	}
	
	public void setLocation(double x, double y) {
		this.oldX = this.x = x;
		this.oldY = this.y = y;
	}
	
	public void move(double dx, double dy) {
		if(dx != 0 || dy != 0) {
			this.oldX = this.x;
			this.x = dx;
			this.oldY = this.y;
			this.y = dy;
		}
	}
	
	public void moveX(double dx) {
		if(dx != 0) {
			this.oldX = this.x;
			this.x = dx;
		}
	}
	
	public void moveY(double dy) {
		if(dy != 0) {
			this.oldY = this.y;
			this.y = dy;
		}
	}
	
	public void setX(double x) {
		this.oldX = this.x = x;
	}
	
	public void setY(double y) {
		this.oldY = this.y = y;
	}
	
	public void forceX(double x) {
		this.x = x;
	}
	
	public void forceY(double y) {
		this.y = y;
	}
	
	public double getX() {
		return this.x;
	}
	
	public double getY() {
		return this.y;
	}
	
	public double getOldX() {
		return this.oldX;
	}
	
	public double getOldY() {
		return this.oldY;
	}
	
	public void setSpeed(double vx, double vy) {
		this.horizontalSpeed = vx;
		this.verticalSpeed = vy;
	}
	
	public void setHorizontalSpeed(double vx) {
		this.horizontalSpeed = vx;
	}
	
	public void setVerticalSpeed(double vy) {
		this.verticalSpeed = vy;
	}
	
	public void setMovement(double speed, double angleDir) {
		double radians = Math.toRadians(angleDir);
		this.setSpeed(Math.sin(radians) * speed, -Math.cos(radians) * speed);
	}
	
	public void addHorizontalSpeed(long elapsedTime, double accel, double maxSpeed) {
		if(accel == 0 || elapsedTime == 0) {
			return;
		}
		
		this.horizontalSpeed += accel * elapsedTime;
		
		if(accel < 0) {
			if(this.horizontalSpeed < maxSpeed) {
				this.horizontalSpeed = maxSpeed;
			}
		} else {
			if(this.horizontalSpeed > maxSpeed) {
				this.horizontalSpeed = maxSpeed;
			}
		}
	}
	
	public void addVerticalSpeed(long elapsedTime, double accel, double maxSpeed) {
		if(accel == 0 || elapsedTime == 0) {
			return;
		}
		
		this.verticalSpeed += accel * elapsedTime;
		
		if(accel < 0) {
			if(this.verticalSpeed < maxSpeed) {
				this.verticalSpeed = maxSpeed;
			}
		} else {
			if(this.verticalSpeed > maxSpeed) {
				this.verticalSpeed = maxSpeed;
			}
		}
	}
	
	public double getHorizontalSpeed() {
		return this.horizontalSpeed;
	}
	
	public double getVerticalSpeed() {
		return this.verticalSpeed;
	}
	
	public void update(long elapsedTime) {
		this.updateMovement(elapsedTime);
	}
	
	public double getScreenX() {
		return this.x - this.background.getX() + this.background.getClip().x;
	}
	
	public double getScreenY() {
		return this.y - this.background.getY() + this.background.getClip().y;
	}
	
	public double getCenterX() {
		return this.x + (this.width/2);
	}
	
	public double getCenterY() {
		return this.y + (this.height/2);
	}
	
	public boolean isOnScreen(int leftOffset, int topOffset, int rightOffset
			, int bottomOffset) {
		Sprite.screenX = this.x = this.background.getX();
		Sprite.screenY = this.y = this.background.getY();
		
		return (Sprite.screenX + this.width > -leftOffset
				&& Sprite.screenY + this.height > -topOffset
				&& Sprite.screenX < this.background.getClip().width
						+ rightOffset && Sprite.screenY < this.background
						.getClip().height + bottomOffset);
	}
	
	public boolean isOnScreen() {
		return this.isOnScreen(0, 0, 0, 0);
	}
	
	protected void updateMovement(long elapsedTime) {
		
	}
	
	public void render(Graphics g) {
		Sprite.screenX = this.x - this.background.getX();
		Sprite.screenY = this.y - this.background.getY();
		
		if(Sprite.screenX + this.width <= 0
				|| Sprite.screenY + this.height <= 0
				|| Sprite.screenX > this.background.getClip().width
				|| Sprite.screenY > this.background.getClip().height) {
			return;
		}
		
		Sprite.screenX += this.background.getClip().x;
		Sprite.screenY += this.background.getClip().y;
		
		this.render(g, (int) Sprite.screenX, (int) Sprite.screenY);
	}
	
	public void render(Graphics g, int x, int y) {
		g.drawImage(this.image, x, y);
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getLayer() {
		return layer;
	}

	public void setLayer(int layer) {
		this.layer = layer;
	}

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public boolean isImmutable() {
		return immutable;
	}

	public void setImmutable(boolean immutable) {
		this.immutable = immutable;
	}
}